﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using ArcEngine.Resources;
using ArcEngine;
using System.Drawing;
using ArcEngine.Graphics;


namespace DungeonEye
{

	/// <summary>
	/// 
	/// </summary>
	public class Maze
	{

		/// <summary>
		/// 
		/// </summary>
		public Maze()
		{
			Walls = new List<List<sbyte>>();
			Monsters = new Dictionary<string, Monster>();
		}


		/// <summary>
		/// Initialize the level
		/// </summary>
		/// <returns></returns>
		public bool Init()
		{
			Tileset = ResourceManager.GetTileSet(TilesetName);
			if (Tileset == null)
				return false;
			Tileset.Zoom = new SizeF(2.0f, 2.0f);

			return true;
		}


		/// <summary>
		/// Update all elements in the level
		/// </summary>
		public void Update()
		{
			// Update monsters
			foreach (Monster monster in Monsters.Values)
				monster.Update();

		}



		/// <summary>
		/// Returns the ID of a wall
		/// </summary>
		/// <param name="location">Location of the wall in the level</param>
		/// <returns>ID of the tile</returns>
		public sbyte GetWallID(Point location)
		{
			// Oops
			if (!Rectangle.Contains(location))
				return -1;

			return Walls[location.Y][location.X];
		}

		/// <summary>
		/// Check if a monsters are present at this location
		/// </summary>
		/// <param name="location"></param>
		/// <returns></returns>
		public List<Monster> GetMonsterAt(Point location)
		{



			return null;
		}

		/// <summary>
		/// Draw the level
		/// </summary>
		/// <param name="location">Location to display frmo</param>
		/// <param name="direction">Direction</param>
		/// <see cref="http://eob.wikispaces.com/eob.vmp"/>
		public void Draw(Point location, Compass direction)
		{
			Video.ScissorZone = new Rectangle(0, 0, 352, 240);
			Video.Scissor = true;


			// The walls
			if (Tileset != null)
			{

				// Draw the dungeon
				Tileset.Bind();
				Video.Color = Color.White;

				// Backdrop
				Tileset.Draw(0, Point.Empty, location.X % 2 == 0, false);

				Video.Scissor = false;

				// Display a map of the level
				for (int y = 0; y < size.Height; y++)
				{
					for (int x = 0; x < size.Width; x++)
					{
						// Walls
						if (GetWallID(new Point(x, y)) == 1)
							Video.Color = Color.White;
						else
							Video.Color = Color.Black;

						Video.Rectangle(new Rectangle(400 + x * 4, 250 + y * 4, 4, 4), true);
					}
				}


				// Monsters
				Video.Color = Color.Blue;
				foreach (Monster monster in Monsters.Values)
				{
					Video.Rectangle(new Rectangle(400 + monster.Location.X * 4, 250 + monster.Location.Y * 4, 4, 4), true);
				}

				Video.Color = Color.Red;
				Video.Rectangle(new Rectangle(400 + location.X * 4, 250 + location.Y * 4, 4, 4), true);
				Video.Color = Color.White;

				Video.Scissor = true;



				//
				// Draws the walls
				//
				ViewField pov = new ViewField(this, location, direction);

				// alternate the wall
				int swap = (location.Y % 2) * 9;

				// row -3
				if (pov.Walls[0] == 1)
					Tileset.Draw(7 + swap, new Point(0, 54));

				if (pov.Walls[6] == 1)
					Tileset.Draw(7 + swap, new Point(336, 54), true, false);

				if (pov.Walls[1] == 1)
				{
					Tileset.Draw(7 + swap, new Point(32, 56));
					Tileset.Draw(3 + swap, new Point(-64, 54));
				}

				if (pov.Walls[5] == 1)
				{
					Tileset.Draw(3 + swap, new Point(320, 54));
					Tileset.Draw(7 + swap, new Point(304, 56), true, false);
				}

				if (pov.Walls[2] == 1)
				{
					Tileset.Draw(3 + swap, new Point(32, 54));
					Tileset.Draw(7 + swap, new Point(128, 56));
				}

				if (pov.Walls[4] == 1)
				{
					Tileset.Draw(3 + swap, new Point(224, 54));
					Tileset.Draw(7 + swap, new Point(208, 56), true, false);
				}

				if (pov.Walls[3] == 1)
					Tileset.Draw(3 + swap, new Point(128, 54));



				// row -2
				if (pov.Walls[7] == 1)
					Tileset.Draw(6 + swap, new Point(0, 40));

				if (pov.Walls[11] == 1)
					Tileset.Draw(6 + swap, new Point(320, 40), true, false);

				if (pov.Walls[8] == 1)
				{
					Tileset.Draw(2 + swap, new Point(-64, 40));
					Tileset.Draw(6 + swap, new Point(96, 40));
				}

				if (pov.Walls[10] == 1)
				{
					Tileset.Draw(2 + swap, new Point(256, 40));
					Tileset.Draw(6 + swap, new Point(224, 40), true, false);
				}

				if (pov.Walls[9] == 1)
					Tileset.Draw(2 + swap, new Point(96, 40));



				// row -1
				if (pov.Walls[12] == 1)
				{
					Tileset.Draw(1 + swap, new Point(-208, 14));
					Tileset.Draw(5 + swap, new Point(48, 16));
				}

				if (pov.Walls[14] == 1)
				{
					Tileset.Draw(1 + swap, new Point(304, 14));
					Tileset.Draw(5 + swap, new Point(256, 16), true, false);
				}

				if (pov.Walls[13] == 1)
					Tileset.Draw(1 + swap, new Point(48, 14));



				// row 0
				if (pov.Walls[15] == 1)
					Tileset.Draw(4 + swap, new Point(0, 0));

				if (pov.Walls[17] == 1)
					Tileset.Draw(4 + swap, new Point(304, 0), true, false);
			}

			Video.Scissor = false;

			// The monsters
			foreach (Monster monster in Monsters.Values)
				monster.Draw(Point.Empty);

		}



		/// <summary>
		/// Checks if a point is visible from another point
		/// ie: is a monster visible from the current point of view of the team ?
		/// </summary>
		/// <param name="from">The point of view</param>
		/// <param name="to">The destination point to check</param>
		/// <param name="dir">The direction to look</param>
		/// <param name="distance">See distance</param>
		/// <returns>true if visible, false if not visible</returns>
		public bool IsVisible(Point from, Point to, Compass dir, int distance)
		{

			return false;
		}



		#region IO

		/// <summary>
		/// 
		/// </summary>
		/// <param name="filename"></param>
		/// <returns></returns>
		public bool Load(XmlNode xml)
		{

			foreach (XmlNode node in xml)
			{
				if (node.NodeType == XmlNodeType.Comment)
					continue;


				switch (node.Name.ToLower())
				{
					case "tileset":
					{
						TilesetName = node.Attributes["name"].Value;
					}
					break;


					// walls
					case "walls":
					{
						size = new Size(int.Parse(node.Attributes["width"].Value), int.Parse(node.Attributes["height"].Value));

						Walls = new List<List<sbyte>>(size.Height);
						for (int y = 0; y < size.Height; y++)
						{
							Walls.Add(new List<sbyte>());
							for (int x = 0; x < size.Width; x++)
								Walls[y].Add(0);
						}

						foreach (XmlNode subnode in node)
						{
							switch (subnode.Name.ToLower())
							{
								// Add a row
								case "row":
								{
									int rowid = Int32.Parse(subnode.Attributes["id"].Value);
									int pos = 0;

									string[] val = subnode.InnerText.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
									foreach (string id in val)
										Walls[rowid][pos++] = sbyte.Parse(id);
								}
								break;
							}
						}

					}
					break;

					// Monster
					case "dwarf":
					{
						Dwarf dwarf = new Dwarf(node.Attributes["name"].Value, this);
						dwarf.Load(node);
						Monsters[dwarf.Name] = dwarf;

					}
					break;
				}
			}



			return true;
		}



		/// <summary>
		/// 
		/// </summary>
		/// <param name="filename"></param>
		/// <returns></returns>
		public bool Save(string filename)
		{
			return false;

		}

		#endregion



		#region Properties

		/// <summary>
		/// Tiles to use
		/// </summary>
		TileSet Tileset;

		/// <summary>
		/// Tileset name to use
		/// </summary>
		public string TilesetName;

		/// <summary>
		/// Contain all the wall of the level
		/// </summary>
		List<List<sbyte>> Walls;

		/// <summary>
		/// Gets the size of the level
		/// </summary>
		public Size Size
		{
			get
			{
				return size;
			}
		}
		Size size;


		/// <summary>
		/// Rectangle of the level
		/// </summary>
		public Rectangle Rectangle
		{
			get
			{
				return new Rectangle(Point.Empty, Size);
			}
		}


		/// <summary>
		/// Monsters in the level
		/// </summary>
		Dictionary<string, Monster> Monsters;
		#endregion


	}

	/// <summary>
	/// Get the walls and the monsters viewed from a point of view
	/// </summary>
	public class ViewField
	{
		/// <summary>
		/// Constructor
		/// </summary>
		public ViewField(Maze level, Point location, Compass direction)
		{
			walls = new sbyte[18];
			monsters = new List<Monster>[18];
			isTeamVisible = false;


			// Cone of vision : 18 blocks
			//
			//   3333333
			//    22222
			//     111
			//     0P0
			//
			// P => Point of view
			switch (direction)
			{
				case Compass.North:
				{
					walls[0] = level.GetWallID(new Point(location.X - 3, location.Y - 3));
					walls[1] = level.GetWallID(new Point(location.X - 2, location.Y - 3));
					walls[2] = level.GetWallID(new Point(location.X - 1, location.Y - 3));
					walls[3] = level.GetWallID(new Point(location.X, location.Y - 3));
					walls[4] = level.GetWallID(new Point(location.X + 1, location.Y - 3));
					walls[5] = level.GetWallID(new Point(location.X + 2, location.Y - 3));
					walls[6] = level.GetWallID(new Point(location.X + 3, location.Y - 3));

					walls[7] = level.GetWallID(new Point(location.X - 2, location.Y - 2));
					walls[8] = level.GetWallID(new Point(location.X - 1, location.Y - 2));
					walls[9] = level.GetWallID(new Point(location.X, location.Y - 2));
					walls[10] = level.GetWallID(new Point(location.X + 1, location.Y - 2));
					walls[11] = level.GetWallID(new Point(location.X + 2, location.Y - 2));

					walls[12] = level.GetWallID(new Point(location.X - 1, location.Y - 1));
					walls[13] = level.GetWallID(new Point(location.X, location.Y - 1));
					walls[14] = level.GetWallID(new Point(location.X + 1, location.Y - 1));

					walls[15] = level.GetWallID(new Point(location.X - 1, location.Y));
					walls[16] = level.GetWallID(new Point(location.X, location.Y));
					walls[17] = level.GetWallID(new Point(location.X + 1, location.Y));
				}
				break;

				case Compass.South:
				{
					walls[0] = level.GetWallID(new Point(location.X + 3, location.Y + 3));
					walls[1] = level.GetWallID(new Point(location.X + 2, location.Y + 3));
					walls[2] = level.GetWallID(new Point(location.X + 1, location.Y + 3));
					walls[3] = level.GetWallID(new Point(location.X, location.Y + 3));
					walls[4] = level.GetWallID(new Point(location.X - 1, location.Y + 3));
					walls[5] = level.GetWallID(new Point(location.X - 2, location.Y + 3));
					walls[6] = level.GetWallID(new Point(location.X - 3, location.Y + 3));

					walls[7] = level.GetWallID(new Point(location.X + 2, location.Y + 2));
					walls[8] = level.GetWallID(new Point(location.X + 1, location.Y + 2));
					walls[9] = level.GetWallID(new Point(location.X, location.Y + 2));
					walls[10] = level.GetWallID(new Point(location.X - 1, location.Y + 2));
					walls[11] = level.GetWallID(new Point(location.X - 2, location.Y + 2));

					walls[12] = level.GetWallID(new Point(location.X + 1, location.Y + 1));
					walls[13] = level.GetWallID(new Point(location.X, location.Y + 1));
					walls[14] = level.GetWallID(new Point(location.X - 1, location.Y + 1));

					walls[15] = level.GetWallID(new Point(location.X + 1, location.Y));
					walls[16] = level.GetWallID(new Point(location.X, location.Y));
					walls[17] = level.GetWallID(new Point(location.X - 1, location.Y));
				}
				break;

				case Compass.East:
				{
					walls[0] = level.GetWallID(new Point(location.X + 3, location.Y - 3));
					walls[1] = level.GetWallID(new Point(location.X + 3, location.Y - 2));
					walls[2] = level.GetWallID(new Point(location.X + 3, location.Y - 1));
					walls[3] = level.GetWallID(new Point(location.X + 3, location.Y));
					walls[4] = level.GetWallID(new Point(location.X + 3, location.Y + 1));
					walls[5] = level.GetWallID(new Point(location.X + 3, location.Y + 2));
					walls[6] = level.GetWallID(new Point(location.X + 3, location.Y + 3));

					walls[7] = level.GetWallID(new Point(location.X + 2, location.Y - 2));
					walls[8] = level.GetWallID(new Point(location.X + 2, location.Y - 1));
					walls[9] = level.GetWallID(new Point(location.X + 2, location.Y));
					walls[10] = level.GetWallID(new Point(location.X + 2, location.Y + 1));
					walls[11] = level.GetWallID(new Point(location.X + 2, location.Y + 2));

					walls[12] = level.GetWallID(new Point(location.X + 1, location.Y - 1));
					walls[13] = level.GetWallID(new Point(location.X + 1, location.Y));
					walls[14] = level.GetWallID(new Point(location.X + 1, location.Y + 1));

					walls[15] = level.GetWallID(new Point(location.X, location.Y - 1));
					walls[16] = level.GetWallID(new Point(location.X, location.Y));
					walls[17] = level.GetWallID(new Point(location.X, location.Y + 1));
				}
				break;

				case Compass.West:
				{
					walls[0] = level.GetWallID(new Point(location.X - 3, location.Y + 3));
					walls[1] = level.GetWallID(new Point(location.X - 3, location.Y + 2));
					walls[2] = level.GetWallID(new Point(location.X - 3, location.Y + 1));
					walls[3] = level.GetWallID(new Point(location.X - 3, location.Y));
					walls[4] = level.GetWallID(new Point(location.X - 3, location.Y - 1));
					walls[5] = level.GetWallID(new Point(location.X - 3, location.Y - 2));
					walls[6] = level.GetWallID(new Point(location.X - 3, location.Y - 3));

					walls[7] = level.GetWallID(new Point(location.X - 2, location.Y + 2));
					walls[8] = level.GetWallID(new Point(location.X - 2, location.Y + 1));
					walls[9] = level.GetWallID(new Point(location.X - 2, location.Y));
					walls[10] = level.GetWallID(new Point(location.X - 2, location.Y - 1));
					walls[11] = level.GetWallID(new Point(location.X - 2, location.Y - 2));

					walls[12] = level.GetWallID(new Point(location.X - 1, location.Y + 1));
					walls[13] = level.GetWallID(new Point(location.X - 1, location.Y));
					walls[14] = level.GetWallID(new Point(location.X - 1, location.Y - 1));

					walls[15] = level.GetWallID(new Point(location.X, location.Y + 1));
					walls[16] = level.GetWallID(new Point(location.X, location.Y));
					walls[17] = level.GetWallID(new Point(location.X, location.Y - 1));
				}
				break;
			}



		}


		/// <summary>
		/// Get the monsters around
		/// </summary>
		public List<Monster>[] Monsters
		{
			get
			{
				return monsters;
			}
		}
		List<Monster>[] monsters;


		/// <summary>
		/// Get the walls around
		/// </summary>
		public sbyte[] Walls
		{
			get
			{
				return walls;
			}
		}
		sbyte[] walls;


		/// <summary>
		/// Is the team visible, no wall between the Team and the Point Of View 
		/// </summary>
		public bool IsTeamVisible
		{
			get
			{
				return isTeamVisible;
			}
		}
		bool isTeamVisible;
	}
}
